# -*- coding: utf-8 -*-

# File Name： pipline
# Description :
# Author : lirui
# date： 2022/5/23
# Change Activity:
# 2022/5/23:
"""
设计模式：pipline模块，具体实现业务可继承Pipline类，程序将根据配置文件逐步对数据进行操作。
save_step_detail=True可保存每一步执行的结果以便调试。

为减少新建对象开销，建议将PipLine 实现对象作为全局变量长期保存至内存，而不是每次使用都新建pipline对象.
"""
import copy
import time
from collections.abc import Iterable

from .step_detail import StepDetail


class PipLine:
    """
    pipline 基类
    """

    def __init__(self, pipline_cfg, module_registry, save_step_detail=False):
        """
        构造pipline对象

        Args:
            pipline_cfg:pipline配置文件
            module_registry:  查找pipline模块的注册对象,可以是对象或数组。如果是数组，遍历该数组查找指定名称的值。
            save_step_detail:  是否保存每步执行细节
        """
        self.pipline_steps = pipline_cfg
        self.module_registry = module_registry
        self.save_step_detail = save_step_detail
        self.transformers = self._build_transformers()
        if save_step_detail:
            self.middle_parameters = []  # 保存中间变量，便于debug

    def _build_transformers(self):
        """
        将所有步骤操作的对象合并为transformers

        """
        transformers = []
        for step_info in self.pipline_steps:
            obj = self.build_step_obj(copy.deepcopy(step_info))
            transformers.append(obj)
        return transformers

    def __call__(self, data):
        """
        执行每一步step

        Args:
            data: 输入数据

        Returns:

        """
        start_time, input_data, = None, None
        for trans in self.transformers:
            if self.save_step_detail:
                start_time = time.time()
                input_data = copy.deepcopy(data)
            data = trans(data)  # handle
            if self.save_step_detail:
                class_name = trans.__class__.__name__
                detail = StepDetail(step_name=class_name, start_time=start_time, end_time=time.time(),
                                    inputs=input_data, outputs=copy.deepcopy(data))
                self.middle_parameters.append(detail)
        return data

    def _get_callable(self, callable_name):
        """
        根据 type 的值查找对象模块

        Args:
            callable_name: 模块名

        Returns:

        """
        if isinstance(self.module_registry, Iterable):  # 处理多个registry合并的情况。
            for registry in self.module_registry:
                if registry.contain(callable_name):
                    return registry.get(callable_name)
        else:
            return self.module_registry.get(callable_name)

    def build_step_obj(self, step_info):
        """
        构建每一步骤对应的对象

        Args:
            step_info: 来源于pipline的配置文件，根据每一步要执行的信息，新建对应执行对象并返回。

        Returns:
            obj: callable 对象
        """
        step_name = step_info.pop('type')
        _callable = self._get_callable(step_name)
        obj = _callable(**step_info)
        return obj
